﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using GeoAPI.Operation.Buffer;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;
using NetTopologySuite.Noding;
using NetTopologySuite.Operation.Overlay;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Operation.Buffer
{
	internal class BufferBuilder
	{
		private static int DepthDelta(Label label)
		{
			Location location = label.GetLocation(0, Positions.Left);
			Location location2 = label.GetLocation(0, Positions.Right);
			if (location == Location.Interior && location2 == Location.Exterior)
			{
				return 1;
			}
			if (location == Location.Exterior && location2 == Location.Interior)
			{
				return -1;
			}
			return 0;
		}

		public BufferBuilder(IBufferParameters bufParams)
		{
			this._bufParams = bufParams;
		}

		public IPrecisionModel WorkingPrecisionModel
		{
			get
			{
				return this._workingPrecisionModel;
			}
			set
			{
				this._workingPrecisionModel = value;
			}
		}

		public INoder Noder
		{
			get
			{
				return this._workingNoder;
			}
			set
			{
				this._workingNoder = value;
			}
		}

		public IGeometry Buffer(IGeometry g, double distance)
		{
			IPrecisionModel precisionModel = this._workingPrecisionModel;
			if (precisionModel == null)
			{
				precisionModel = g.PrecisionModel;
			}
			this._geomFact = g.Factory;
			OffsetCurveBuilder curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
			IList<ISegmentString> curves = new OffsetCurveSetBuilder(g, distance, curveBuilder).GetCurves();
			if (curves.Count <= 0)
			{
				return this.CreateEmptyResultGeometry();
			}
			this.ComputeNodedEdges(curves, precisionModel);
			this._graph = new PlanarGraph(new OverlayNodeFactory());
			this._graph.AddEdges(this._edgeList.Edges);
			IEnumerable<BufferSubgraph> subgraphList = BufferBuilder.CreateSubgraphs(this._graph);
			PolygonBuilder polygonBuilder = new PolygonBuilder(this._geomFact);
			BufferBuilder.BuildSubgraphs(subgraphList, polygonBuilder);
			IList<IGeometry> polygons = polygonBuilder.Polygons;
			if (polygons.Count <= 0)
			{
				return this.CreateEmptyResultGeometry();
			}
			return this._geomFact.BuildGeometry(polygons);
		}

		private INoder GetNoder(IPrecisionModel precisionModel)
		{
			if (this._workingNoder != null)
			{
				return this._workingNoder;
			}
			return new MCIndexNoder(new IntersectionAdder(new RobustLineIntersector
			{
				PrecisionModel = precisionModel
			}));
		}

		private void ComputeNodedEdges(IList<ISegmentString> bufferSegStrList, IPrecisionModel precisionModel)
		{
			INoder noder = this.GetNoder(precisionModel);
			noder.ComputeNodes(bufferSegStrList);
			foreach (ISegmentString segmentString in noder.GetNodedSubstrings())
			{
				Coordinate[] coordinates = segmentString.Coordinates;
				if (coordinates.Length != 2 || !coordinates[0].Equals2D(coordinates[1]))
				{
					Label lbl = (Label)segmentString.Context;
					Edge e = new Edge(segmentString.Coordinates, new Label(lbl));
					this.InsertUniqueEdge(e);
				}
			}
		}

		protected void InsertUniqueEdge(Edge e)
		{
			Edge edge = this._edgeList.FindEqualEdge(e);
			if (edge != null)
			{
				Label label = edge.Label;
				Label label2 = e.Label;
				if (!edge.IsPointwiseEqual(e))
				{
					label2 = new Label(e.Label);
					label2.Flip();
				}
				label.Merge(label2);
				int num = BufferBuilder.DepthDelta(label2);
				int depthDelta = edge.DepthDelta + num;
				edge.DepthDelta = depthDelta;
				return;
			}
			this._edgeList.Add(e);
			e.DepthDelta = BufferBuilder.DepthDelta(e.Label);
		}

		private static IEnumerable<BufferSubgraph> CreateSubgraphs(PlanarGraph graph)
		{
			List<BufferSubgraph> list = new List<BufferSubgraph>();
			foreach (Node node in graph.Nodes)
			{
				if (!node.IsVisited)
				{
					BufferSubgraph bufferSubgraph = new BufferSubgraph();
					bufferSubgraph.Create(node);
					list.Add(bufferSubgraph);
				}
			}
			list.Sort();
			list.Reverse();
			return list;
		}

		private static void BuildSubgraphs(IEnumerable<BufferSubgraph> subgraphList, PolygonBuilder polyBuilder)
		{
			List<BufferSubgraph> list = new List<BufferSubgraph>();
			foreach (BufferSubgraph bufferSubgraph in subgraphList)
			{
				Coordinate rightMostCoordinate = bufferSubgraph.RightMostCoordinate;
				int depth = new SubgraphDepthLocater(list).GetDepth(rightMostCoordinate);
				bufferSubgraph.ComputeDepth(depth);
				bufferSubgraph.FindResultEdges();
				list.Add(bufferSubgraph);
				polyBuilder.Add(new List<EdgeEnd>(Caster.Upcast<DirectedEdge, EdgeEnd>(bufferSubgraph.DirectedEdges)), bufferSubgraph.Nodes);
			}
		}

		private static IGeometry ConvertSegStrings(IEnumerator<ISegmentString> it)
		{
			GeometryFactory geometryFactory = new GeometryFactory();
			List<IGeometry> list = new List<IGeometry>();
			while (it.MoveNext())
			{
				ISegmentString segmentString = it.Current;
				ILineString item = geometryFactory.CreateLineString(segmentString.Coordinates);
				list.Add(item);
			}
			return geometryFactory.BuildGeometry(list);
		}

		private IGeometry CreateEmptyResultGeometry()
		{
			return this._geomFact.CreatePolygon(null, null);
		}

		private readonly IBufferParameters _bufParams;

		private IPrecisionModel _workingPrecisionModel;

		private INoder _workingNoder;

		private IGeometryFactory _geomFact;

		private PlanarGraph _graph;

		private readonly EdgeList _edgeList = new EdgeList();
	}
}
